/* Copyright (c) 2022 渝州大数据实验室
 *
 * Lanius is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *     http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.yzbdl.lanius.orchestrate.serv.service.resource.impl;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.yzbdl.lanius.orchestrate.common.dto.resource.DatabaseConfigurationDTO;
import org.yzbdl.lanius.orchestrate.common.dto.resource.TaskResourceConfigDTO;
import org.yzbdl.lanius.orchestrate.common.dto.resource.TaskResourceConfigPageDto;
import org.yzbdl.lanius.orchestrate.common.entity.resource.TaskResourceConfigEntity;
import org.yzbdl.lanius.orchestrate.common.entity.resource.TaskResourceEntity;
import org.yzbdl.lanius.orchestrate.common.enums.ResourceDataStatusEnum;
import org.yzbdl.lanius.orchestrate.common.exception.runtime.BusinessException;
import org.yzbdl.lanius.orchestrate.common.utils.AesUtils;
import org.yzbdl.lanius.orchestrate.common.utils.ExceptionUtil;
import org.yzbdl.lanius.orchestrate.common.utils.MessageUtil;
import org.yzbdl.lanius.orchestrate.common.utils.SpecialCharacterUtil;
import org.yzbdl.lanius.orchestrate.serv.database.DbRepositoryFactory;
import org.yzbdl.lanius.orchestrate.serv.database.DbRepositoryInvokeHandler;
import org.yzbdl.lanius.orchestrate.serv.mapper.resource.TaskResourceConfigMapper;
import org.yzbdl.lanius.orchestrate.serv.mapper.resource.TaskResourceMapper;
import org.yzbdl.lanius.orchestrate.serv.service.resource.TaskResourceConfigService;
import org.yzbdl.lanius.orchestrate.serv.service.resource.TaskResourceService;
import org.yzbdl.lanius.orchestrate.serv.utils.ValidationUtils;

import java.util.Objects;

/**
 * @author zhuhongji@yzbdl.ac.cn
 * @since 2022-04-08 16:14
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TaskResourceConfigServiceImpl extends ServiceImpl<TaskResourceConfigMapper, TaskResourceConfigEntity> implements TaskResourceConfigService {

	private final TaskResourceMapper taskResourceMapper;

	private final TaskResourceService taskResourceService;

	@Value("${encryption.user-key}")
	String encryptKey;

	@Override
	public Boolean addTaskResourceConfig(TaskResourceConfigDTO taskResourceConfigDTO) {
		// 重命名判断
		LambdaQueryWrapper<TaskResourceConfigEntity> queryWrapper = new QueryWrapper<TaskResourceConfigEntity>().lambda()
				.eq(TaskResourceConfigEntity::getConnectName, taskResourceConfigDTO.getConnectName())
				.eq(TaskResourceConfigEntity::getDeleted, false);
		ExceptionUtil.checkParam(!this.baseMapper.exists(queryWrapper), "已存在同样的资源库名称，请重新输入！");
		LambdaQueryWrapper<TaskResourceConfigEntity> query = new QueryWrapper<TaskResourceConfigEntity>().lambda()
				.eq(TaskResourceConfigEntity::getConnectUrl, taskResourceConfigDTO.getConnectUrl())
				.eq(TaskResourceConfigEntity::getConnectAccount, taskResourceConfigDTO.getConnectAccount())
				.eq(TaskResourceConfigEntity::getDeleted, false);
		ExceptionUtil.checkParam(!this.baseMapper.exists(query),
				"已存在链接配置、连接用户两项同时相同的资源配置，请重新输入！");
		if(Objects.isNull(taskResourceConfigDTO.getStatus())){
			taskResourceConfigDTO.setStatus(ResourceDataStatusEnum.NORMAL.getCode());
		}
		taskResourceConfigDTO.setDeleted(false);
		TaskResourceConfigEntity taskResourceConfigEntity = new TaskResourceConfigEntity();
		BeanUtils.copyProperties(taskResourceConfigDTO, taskResourceConfigEntity);
		return this.save(taskResourceConfigEntity);
	}

	@Override
	public Boolean deleteTaskResourceConfig(Long id) {
		LambdaQueryWrapper<TaskResourceEntity> queryWrapper = new QueryWrapper<TaskResourceEntity>().lambda()
				.eq(TaskResourceEntity::getResourceConfigId, id)
				.eq(TaskResourceEntity::getDeleted, false);
		if(taskResourceMapper.exists(queryWrapper)){
			throw new BusinessException("该资源库中数据已被任务调用！");
		}
		return this.updateById(TaskResourceConfigEntity.builder().id(id).deleted(true).build());
	}

	@Override
	public TaskResourceConfigEntity getTaskResourceConfig(Long id) {
		TaskResourceConfigEntity getOne = this.getById(id);
		if(ObjectUtil.isNull(getOne)){
			getOne.setConnectPassword(AesUtils.encrypt(getOne.getConnectPassword(), encryptKey));
		}
		return getOne;
	}

    @Override
    public IPage<TaskResourceConfigDTO> pageTaskResourceConfig(Integer page, Integer size,
        TaskResourceConfigPageDto taskResourceConfigPageDto) {
        Page<TaskResourceConfigEntity> pageParam = new Page<>(page, size);
        LambdaQueryWrapper<TaskResourceConfigEntity> queryWrapper =
            new QueryWrapper<TaskResourceConfigEntity>().lambda()
                // 模糊匹配资源库名称
                .like(StringUtils.hasLength(taskResourceConfigPageDto.getConnectName()),
                    TaskResourceConfigEntity::getConnectName, SpecialCharacterUtil.escapeStr(taskResourceConfigPageDto.getConnectName()))
                .eq(TaskResourceConfigEntity::getDeleted, false)
		        // 筛选任务资源配置状态
                .eq(Objects.nonNull(taskResourceConfigPageDto.getStatus()), TaskResourceConfigEntity::getStatus,
                    taskResourceConfigPageDto.getStatus())
                .orderByDesc(TaskResourceConfigEntity::getCreateTime).orderByDesc(TaskResourceConfigEntity::getId);
		return this.page(pageParam, queryWrapper)
				.convert(taskResourceConfigEntity -> {
					TaskResourceConfigDTO taskResourceConfigDTO = new TaskResourceConfigDTO();
					BeanUtils.copyProperties(taskResourceConfigEntity, taskResourceConfigDTO);
					String password = AesUtils.encrypt(taskResourceConfigEntity.getConnectPassword(), encryptKey);
					taskResourceConfigDTO.setConnectPassword(password);
					return taskResourceConfigDTO;
				});
    }

	@Override
	public Boolean updateTaskResourceConfig(TaskResourceConfigEntity taskResourceConfigEntity) {
		ExceptionUtil.checkParam(Objects.nonNull(taskResourceConfigEntity.getId()), "id不能为空");
		// 重命名判断
		LambdaQueryWrapper<TaskResourceConfigEntity> queryWrapper = new QueryWrapper<TaskResourceConfigEntity>().lambda()
				.ne(TaskResourceConfigEntity::getId, taskResourceConfigEntity.getId())
				.eq(TaskResourceConfigEntity::getConnectName, taskResourceConfigEntity.getConnectName())
				.eq(TaskResourceConfigEntity::getDeleted, false);
		ExceptionUtil.checkParam(!this.baseMapper.exists(queryWrapper), "已存在同样的资源库名称，请重新输入！");
		LambdaQueryWrapper<TaskResourceConfigEntity> query = new QueryWrapper<TaskResourceConfigEntity>().lambda()
				.ne(TaskResourceConfigEntity::getId, taskResourceConfigEntity.getId())
				.eq(TaskResourceConfigEntity::getConnectUrl, taskResourceConfigEntity.getConnectUrl())
				.eq(TaskResourceConfigEntity::getConnectAccount, taskResourceConfigEntity.getConnectAccount())
				.eq(TaskResourceConfigEntity::getDeleted, false);
		ExceptionUtil.checkParam(!this.baseMapper.exists(query),
				"已存在链接配置、连接用户两项同时相同的资源配置，请重新输入！");
		// 更新任务资源相关数据
		TaskResourceConfigEntity oldEntity = this.getById(taskResourceConfigEntity.getId());
		if(StringUtils.hasLength(taskResourceConfigEntity.getConnectUrl()) &&
				!taskResourceConfigEntity.getConnectUrl().equals(oldEntity.getConnectUrl())){
			// 获取资源库名称
			DatabaseConfigurationDTO databaseConfigurationDTO = JSONUtil.toBean(taskResourceConfigEntity.getConnectUrl(),
					DatabaseConfigurationDTO.class);
			taskResourceService.batchUpdateResourceContent(taskResourceConfigEntity.getId(),
					databaseConfigurationDTO.getDbName());
		}
		return this.updateById(taskResourceConfigEntity);
	}

	@Override
	public Boolean testConnection(TaskResourceConfigDTO taskResourceConfigDTO) {

		DatabaseConfigurationDTO databaseConfigurationDTO;
		try{
			databaseConfigurationDTO =
								  JSONUtil.toBean(taskResourceConfigDTO.getConnectUrl(), DatabaseConfigurationDTO.class);
		} catch (Exception e){
			log.error("[解析资源库数据失败！],[资源库Json配置解析异常],[请重新配置资源库数据！]");
			throw new BusinessException("资源库链接配置不正确，请重新输入！");
		}
		// 校验资源库链接参数
		ValidationUtils.validateEntity(databaseConfigurationDTO);

		// 获取数据库资源处理类
		DbRepositoryInvokeHandler handler
				= DbRepositoryFactory.getHandler(taskResourceConfigDTO.getConnectCategory());
		if(Objects.isNull(handler)) {
			throw new BusinessException(MessageUtil.get("task.resource.retrieve.handler.fail"));
		}

		// 生成jdbcUrl
		String jdbcUrl = handler.generateJdbcUrl(handler.getJdbcTemplate(org.apache.commons.lang3.StringUtils.EMPTY),
				databaseConfigurationDTO.getIp(), databaseConfigurationDTO.getPort(),
				databaseConfigurationDTO.getDbName(), databaseConfigurationDTO.getParams());
		return handler.testConnection(jdbcUrl, taskResourceConfigDTO.getConnectAccount(),
				taskResourceConfigDTO.getConnectPassword());
	}


}
